package edu.hawaii.webspider;

import java.net.URL;
import java.util.EmptyStackException;
import java.util.logging.Logger;
import com.meterware.httpunit.GetMethodWebRequest;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebLink;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;

/**
 * A demonstration class illustrating how to retrieve and process web pages using HttpUnit.
 * 
 * @author Philip Johnson, modified by: Ivan Wu
 */
public class WebSpiderExample {

  /** Contains the starting URL for the crawl. */
  private final String startUrl;
  /** Global variables for keep-checking the maximum pages. */
  private static int maximumPages = 100;
  /** Global variables for keep-checking the current pages. */
  private static int currentPagaNumber = 0;
  /** Array that stores the visited links. */
  private static String[] linksVisited = new String[65536];
  /** Number that stores the number of links that point back to it. */
  private static int[] numberOfVisited = new int[65536];
  /** Number that keep-checking the index of the array of visited links. */
  private static int indexOfArray = 0;
  /** Check to see if the -logging command argument entered. */
  private static boolean logginState = false;

  /**
   * A WebSpider crawls the web and returns info.
   * 
   * @param startUrl The home url for the crawl.
   */
  public WebSpiderExample(String startUrl) {
    this.startUrl = startUrl;
  }

  /**
   * Returns the links of the URL found at the start URL.
   * 
   * @return The array of of links.
   * @throws Exception if problems occur retrieving the startUrl.
   */
  public WebLink[] getStartLinks() throws Exception {
    // create the conversation object which will maintain state for us
    WebConversation wc = new WebConversation();
    WebRequest request = new GetMethodWebRequest(this.startUrl);
    WebResponse response = wc.getResponse(request);
    return response.getLinks();
  }

  /**
   * Returns the number of links found at the startUrl.
   * 
   * @param newLinks The list of links that need to get number of links.
   * @return The number of links.
   * @throws Exception if problems occur retrieving the startUrl.
   */
  public static int recusivelyGetTotalNumberOfLinks(WebLink[] newLinks) throws Exception {
    // get the number of links in this page
    int totalNumberOfLinks = newLinks.length;
    // create a logger
    Logger logger = Logger.getLogger("edu.hawaii.webspider.webspider");
    // indicating the link if it had visited
    boolean ifVisited = false;
    // catch the exception for a dead link
    try {
      // argument of logging command found
      if (logginState) {
        System.out.println("\n");
        String numberstring = Integer.toString(totalNumberOfLinks);
        logger.info(" Found " + numberstring + " links.\n");
      }
      // increase the number of pages
      currentPagaNumber++;
      // not a base case
      if (totalNumberOfLinks != 0) {
        for (int i = 0; i < newLinks.length && currentPagaNumber < maximumPages; i++) {
          try {
            WebResponse currentURL = newLinks[i].click();
            try {
              URL urlString = currentURL.getURL();
              try {
                String currentURLString = urlString.getProtocol() + "://" + urlString.getHost()
                    + urlString.getPath();
                // Check if the URL visited.
                ifVisited = false;
                for (int j = 0; j < (indexOfArray - 1) && (!ifVisited); j++) {
                  // check if the current URL is visited
                  if (currentURLString.compareTo(linksVisited[j]) == 0) {
                    // Increase the number of visited.
                    numberOfVisited[j]++;
                    // Set the boolean value to true get out of the loop.
                    ifVisited = true;
                  }
                }
                // Not visited.
                if (!ifVisited) {
                  // add the current link to the visited array
                  linksVisited[indexOfArray] = currentURLString;
                  // If the array still has space, add one
                  if (indexOfArray < 65535) {
                    indexOfArray++;
                  }
                  else {
                    System.out.println("Array out of bound.");
                    // Terminate the program
                    maximumPages = currentPagaNumber - 1;
                  }
                  // Set the number of visited.
                  if (logginState) {
                    System.out.println("\n");
                    logger.info("   Retrieving " + currentURLString + "\n");
                  }
                  WebSpiderExample example = new WebSpiderExample(currentURLString);
                  WebLink[] nextLinks = example.getStartLinks();
                  totalNumberOfLinks = totalNumberOfLinks
                      + recusivelyGetTotalNumberOfLinks(nextLinks);
                }
              }
              catch (Exception e) {
                System.out.println("getProtocol, or getHost, or getPath" + e);
                totalNumberOfLinks = 0;
              }
            }
            catch (Exception e) {
              System.out.println("Exception caught: " + e);
              totalNumberOfLinks = 0;
            }
          }
          catch (Exception e) {
            System.out.println("Request rejected!!" + e);
            totalNumberOfLinks = 0;
          }
        }
      }
    }
    catch (Exception e) {
      System.out.println("Fail to get connection: " + e);
    }
    return totalNumberOfLinks;
  }

  /**
   * This method is created for test the main method.
   * 
   * @param args The input command arguments
   * @return args[4] the total number of links
   */
  public static String testMainMethod(String[] args) {
    main(args);
    return args[4];
  }

  /**
   * Retrieves the httpunit home page and counts the number of links on it.
   * 
   * @param args Ignored.
   * @throws EmptyStackException If problems occur.
   */
  public static void main(String[] args) throws EmptyStackException {
    // strings for arguments variables
    String nameOfUrl = "http://www.hackystat.org";
    String nameOfReturnType = "-totallinks";
    String numberOfTotalPages = "100";
    String loggin = "-logging";
    boolean valid = false;
    boolean fiveArguments = false;
    int totalNumberOfLinks = 0;
    // get no arguments variables
    if (args.length == 0) {
      nameOfReturnType = "-totallinks";
      nameOfUrl = "http://www.hackystat.org";
      numberOfTotalPages = "100";
      maximumPages = Integer.valueOf(numberOfTotalPages).intValue();
      valid = true;
    }
    // get three arguments variables
    else if (args.length == 3) {
      nameOfReturnType = args[0];
      nameOfUrl = args[1];
      numberOfTotalPages = args[2];
      maximumPages = Integer.valueOf(args[2]).intValue();
      valid = true;
    }
    // get four arguments variables
    else if (args.length == 4) {
      nameOfReturnType = args[0];
      nameOfUrl = args[1];
      numberOfTotalPages = args[2];
      maximumPages = Integer.valueOf(args[2]).intValue();
      loggin = args[3];
      if (loggin.compareTo("-logging") == 0) {
        logginState = true;
      }
      valid = true;
    }
    // get four arguments variables
    else if (args.length == 5) {
      nameOfReturnType = args[0];
      nameOfUrl = args[1];
      numberOfTotalPages = args[2];
      maximumPages = Integer.valueOf(args[2]).intValue();
      loggin = args[3];
      if (loggin.compareTo("-logging") == 0) {
        logginState = true;
      }
      valid = true;
      fiveArguments = true;
    }
    // invalid of arguments' format
    else {
      System.out.println("Command arguments error!!");
      System.out.println("Enter: java -jar webspider-ivanw.jar"
          + " totallinks http://wwww.hackystat.org 100");
      valid = false;
    }
    if (valid) {
      // create the conversation object which will maintain state for us
      WebSpiderExample example = new WebSpiderExample(nameOfUrl);
      // get the list of the links with the start URL
      try {
        WebLink[] startLinks = example.getStartLinks();
        try {
          // get the total number of links from the start URL
          // plus one for the link of the start URL
          totalNumberOfLinks = recusivelyGetTotalNumberOfLinks(startLinks) + 1;
          if (nameOfReturnType.equalsIgnoreCase("-totallinks")) {
            System.out.println("The total links is: " + totalNumberOfLinks);
            if (valid && fiveArguments) {
              // passing the number of total links to args[4] for test main method.
              args[4] = Integer.toString(totalNumberOfLinks);
            }
          }
          else if (nameOfReturnType.equalsIgnoreCase("-mostpopular")) {
            int maximumNumber = numberOfVisited[0];
            for (int k = 0; k < indexOfArray; k++) {
              if (maximumNumber < numberOfVisited[k]) {
                maximumNumber = numberOfVisited[k];
              }
            }
            for (int m = 0; m < indexOfArray; m++) {
              if (maximumNumber == numberOfVisited[m]) {
                System.out.println("The most popular page is:\n " + linksVisited[m] + " with "
                    + maximumNumber + " pages that point to it.");
              }
            }
          }
          else {
            System.out.println("Unknown Arguments!");
          }
        }
        catch (Exception e) {
          System.out.println("Exception caught: " + e);
        }
      }
      catch (Exception e) {
        System.out.println("Exception caught: " + e);
      }
    }
  }
}
